package com.vast.metrix.viewer.bustraffic.viewers;

import java.awt.Color;
import java.awt.Font;

import org.jfree.chart.annotations.XYAnnotation;
import org.jfree.chart.annotations.XYPointerAnnotation;
import org.jfree.chart.annotations.XYTitleAnnotation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.block.BlockBorder;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.AbstractRenderer;
import org.jfree.chart.renderer.xy.DefaultXYItemRenderer;
import org.jfree.chart.renderer.xy.StandardXYBarPainter;
import org.jfree.chart.renderer.xy.XYAreaRenderer;
import org.jfree.chart.renderer.xy.XYAreaRenderer2;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYStepAreaRenderer;
import org.jfree.chart.title.LegendTitle;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.time.FixedMillisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.TextAnchor;

public class BusTrafficPlotter {
	private String     busName;
	private XYPlot     subPlot;
	private NumberAxis rangeAxisLeftside;
	private NumberAxis rangeAxisRightside;
	private NumberAxis rangeAxisRightside2;
	private boolean    isMaximumItemAge_ms = true;
	private boolean    isAutoRange         = false;
	private int        maximumItemAge_ms   = 500;
	private int        samplingRate_ms = 10;
	private double     upperRangeAxisLeftside;
	private double     upperRangeAxisRightside;
	private double     upperRangeAxisRightside2;

	private XYTitleAnnotation indexAnnotation         = null;
	private XYTitleAnnotation timeAnnotation          = null;
	private XYTitleAnnotation transferByteAnnotation  = null;
	private XYTitleAnnotation activityAnnotation      = null;
	private XYTitleAnnotation latencyAnnotation       = null;
	private XYTitleAnnotation utilizationAnnotation   = null;
	private TimeSeries transferByteSeries;
	private TimeSeries activitySeries;
	private TimeSeries latencySeries;
	private TimeSeries utilizationSeries;
	private TimeSeriesCollection transferByteDataset;
	private TimeSeriesCollection activityDataset;
	private TimeSeriesCollection latencyAndUtilizationDataset;
	
	public BusTrafficPlotter(String busName, double upperRangeAxisLeftside, double upperRangeAxisRightside, double upperRangeAxisRightside2, int samplingRate_ms) {
		createChart(busName, upperRangeAxisLeftside, upperRangeAxisRightside, upperRangeAxisRightside2, samplingRate_ms, maximumItemAge_ms, this.isMaximumItemAge_ms, this.isAutoRange);
	}
	
	public BusTrafficPlotter(String busName, double upperRangeAxisLeftside, double upperRangeAxisRightside, double upperRangeAxisRightside2, int samplingRate_ms, int maximumItemAge_ms) {
		createChart(busName, upperRangeAxisLeftside, upperRangeAxisRightside, upperRangeAxisRightside2, samplingRate_ms, maximumItemAge_ms, this.isMaximumItemAge_ms, this.isAutoRange);
	}
	
	public BusTrafficPlotter(String busName, double upperRangeAxisLeftside, double upperRangeAxisRightside, double upperRangeAxisRightside2, int samplingRate_ms, int maximumItemAge_ms, boolean isMaximumItemAge_ms, boolean isAutoRange) {
		createChart(busName, upperRangeAxisLeftside, upperRangeAxisRightside, upperRangeAxisRightside2, samplingRate_ms, maximumItemAge_ms, isMaximumItemAge_ms, isAutoRange);
	}
	
	public void createChart(String busName, double upperRangeAxisLeftside, double upperRangeAxisRightside, double upperRangeAxisRightside2, int samplingRate_ms, int maximumItemAge_ms, boolean isMaximumItemAge_ms, boolean isAutoRange) {
		this.isMaximumItemAge_ms      = isMaximumItemAge_ms;
		this.isAutoRange              = isAutoRange;
		this.busName                  = busName;
		this.samplingRate_ms          = samplingRate_ms;
		this.maximumItemAge_ms        = maximumItemAge_ms;
		this.upperRangeAxisLeftside   = upperRangeAxisLeftside;
		this.upperRangeAxisRightside  = upperRangeAxisRightside;
		this.upperRangeAxisRightside2 = upperRangeAxisRightside2;
		this.transferByteSeries      = new TimeSeries("TransferByte");
		this.latencySeries           = new TimeSeries("Latency");
		this.utilizationSeries       = new TimeSeries("Utilization");
		this.activitySeries          = new TimeSeries("Activity");
		
		if(this.isMaximumItemAge_ms) {
			this.transferByteSeries.setMaximumItemAge(this.maximumItemAge_ms);
			this.activitySeries.setMaximumItemAge(this.maximumItemAge_ms);
			this.latencySeries.setMaximumItemAge(this.maximumItemAge_ms);
			this.utilizationSeries.setMaximumItemAge(this.maximumItemAge_ms);
		}
	
		this.transferByteDataset          = new TimeSeriesCollection(this.transferByteSeries);
		this.activityDataset              = new TimeSeriesCollection(this.activitySeries);
		this.latencyAndUtilizationDataset = new TimeSeriesCollection(this.latencySeries);
		this.latencyAndUtilizationDataset.addSeries(this.utilizationSeries);
		
		/*
		 *  Define rangeAxis(Y-Axis) at left side
		 */
		this.rangeAxisLeftside = new NumberAxis("[bytes/" + this.samplingRate_ms + "ms]");
		this.rangeAxisLeftside.setTickLabelFont(new Font("SansSerif", Font.PLAIN, 9));
		this.rangeAxisLeftside.setLabelFont(new Font("SansSerif", Font.PLAIN, 9));
		this.rangeAxisLeftside.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
		if(this.isAutoRange) {
			this.rangeAxisLeftside.setAutoRange(true);
		} else {
			this.rangeAxisLeftside.setAutoRange(false);
			this.rangeAxisLeftside.setRange(0, this.upperRangeAxisLeftside);
		}
		this.rangeAxisLeftside.setAutoRangeIncludesZero(false);

		/*
		 * Define sub plot
		 */
		/* Transfer Byte */
		DefaultXYItemRenderer rendererTransfer = new DefaultXYItemRenderer();
		rendererTransfer.setBaseToolTipGenerator(new StandardXYToolTipGenerator());
		this.subPlot = new XYPlot(this.transferByteDataset, null, rangeAxisLeftside,	rendererTransfer);   
		this.subPlot.setBackgroundPaint(Color.black);
		this.subPlot.setDomainGridlinePaint(Color.green);
		this.subPlot.setRangeGridlinePaint(Color.green);
		
		/* Activity */
		this.subPlot.setDataset(1, this.activityDataset);
		this.rangeAxisRightside = new NumberAxis("[times/" + this.samplingRate_ms + "ms]");
		this.subPlot.setRenderer(1, new DefaultXYItemRenderer());
		this.subPlot.setRangeAxis(1, this.rangeAxisRightside);
		this.subPlot.mapDatasetToRangeAxis(1, 1);
		
		/* Latency and Utilization */
		this.subPlot.setDataset(2, this.latencyAndUtilizationDataset);
		this.rangeAxisRightside2 = new NumberAxis("[ticks/" + this.samplingRate_ms + "ms]");
		this.subPlot.setRenderer(2, new XYStepAreaRenderer(XYStepAreaRenderer.AREA));
	       
		//this.subPlot.setRenderer(2, new XYAreaRenderer2());
		this.subPlot.setRangeAxis(2, this.rangeAxisRightside2);
		this.subPlot.mapDatasetToRangeAxis(2, 2);
			
		/* Range Axis*/		
		if(this.isAutoRange) {
			this.rangeAxisRightside.setAutoRange(true);
		} else {
			this.rangeAxisRightside.setAutoRange(false);
			this.rangeAxisRightside.setRange(0, this.upperRangeAxisRightside);
		}
		rangeAxisRightside.setAutoRangeIncludesZero(false);
		
		if(this.isAutoRange) {
			this.rangeAxisRightside2.setAutoRange(true);
		} else {
			this.rangeAxisRightside2.setAutoRange(false);
			this.rangeAxisRightside2.setRange(0, this.upperRangeAxisRightside2);
		}
		rangeAxisRightside2.setAutoRangeIncludesZero(false);
		
		/* Renderer */
		AbstractRenderer r1 = (AbstractRenderer) subPlot.getRenderer(0);
		AbstractRenderer r2 = (AbstractRenderer) subPlot.getRenderer(1);
		AbstractRenderer r3 = (AbstractRenderer) subPlot.getRenderer(2);
		
		r1.setSeriesPaint(0, Color.red, false);
		((DefaultXYItemRenderer)r1).setSeriesShapesVisible(0, false);
		
		r2.setSeriesPaint(0, Color.magenta, false);
		((DefaultXYItemRenderer)r2).setSeriesShapesVisible(0, false);
		
		r3.setSeriesPaint(0, Color.yellow, false);
		r3.setSeriesPaint(1, Color.blue, false);
		
		/* Legend inside the graph */
		LegendTitle lt = new LegendTitle(subPlot);
        lt.setItemFont(new Font("SansSerif", Font.PLAIN, 9));
        lt.setBackgroundPaint(new Color(225, 225, 255, 126));
        lt.setFrame(new BlockBorder(Color.lightGray));
        lt.setPosition(RectangleEdge.BOTTOM);
        
        XYTitleAnnotation ta = new XYTitleAnnotation(0.98, 0.02, lt, RectangleAnchor.BOTTOM_RIGHT);
        ta.setMaxWidth(0.9);
        subPlot.addAnnotation(ta);
        
        TextTitle graphTitle = new TextTitle(this.busName);
        graphTitle.setFont(new Font("SansSerif", Font.PLAIN, 9));
        graphTitle.setPaint(Color.white);
        //graphTitle.setBackgroundPaint(new Color(255, 255, 255, 255));
        //graphTitle.setFrame(new BlockBorder(Color.blue));
        graphTitle.setPosition(RectangleEdge.BOTTOM);
        XYTitleAnnotation graphTitleAnnotation = new XYTitleAnnotation(0.02, 0.98, graphTitle, RectangleAnchor.TOP_LEFT);
        graphTitleAnnotation.setMaxWidth(0.9);
        this.subPlot.addAnnotation(graphTitleAnnotation);
        initSubInformation(0.75);
	}
	
	public void initSubInformation(double x) {	
		TextTitle indexInfo        = new TextTitle("Index: 1");
		indexInfo.setFont(new Font("SansSerif", Font.PLAIN, 9));
		indexInfo.setPaint(Color.GREEN);
		indexInfo.setPosition(RectangleEdge.BOTTOM);
		this.indexAnnotation = new XYTitleAnnotation(x, 0.98, indexInfo, RectangleAnchor.TOP_LEFT);
		this.indexAnnotation.setMaxWidth(0.9);
        this.subPlot.addAnnotation(this.indexAnnotation, false);
        
		TextTitle timeInfo         = new TextTitle("Time:50:20:30:00");
		timeInfo.setFont(new Font("SansSerif", Font.PLAIN, 9));
		timeInfo.setPaint(Color.GREEN);
		timeInfo.setPosition(RectangleEdge.BOTTOM);
		this.transferByteAnnotation = new XYTitleAnnotation(x, 0.88, timeInfo, RectangleAnchor.TOP_LEFT);
        this.transferByteAnnotation.setMaxWidth(0.9);
        this.subPlot.addAnnotation(this.transferByteAnnotation, false);
        
        TextTitle transferByteInfo = new TextTitle("Tran.:");
		transferByteInfo.setFont(new Font("SansSerif", Font.PLAIN, 9));
		transferByteInfo.setPaint(Color.GREEN);
		transferByteInfo.setPosition(RectangleEdge.BOTTOM);
		this.activityAnnotation = new XYTitleAnnotation(x, 0.78, transferByteInfo, RectangleAnchor.TOP_LEFT);
		this.activityAnnotation.setMaxWidth(0.9);
        this.subPlot.addAnnotation(this.activityAnnotation, false);
        
        TextTitle activityInfo     = new TextTitle("Act.:");
		activityInfo.setFont(new Font("SansSerif", Font.PLAIN, 9));
		activityInfo.setPaint(Color.GREEN);
		activityInfo.setPosition(RectangleEdge.BOTTOM);
		this.activityAnnotation = new XYTitleAnnotation(x, 0.68, activityInfo, RectangleAnchor.TOP_LEFT);
		this.activityAnnotation.setMaxWidth(0.9);
        this.subPlot.addAnnotation(this.activityAnnotation, false);
        
        TextTitle latencyInfo      = new TextTitle("Lat.:");
		latencyInfo.setFont(new Font("SansSerif", Font.PLAIN, 9));
		latencyInfo.setPaint(Color.GREEN);
		latencyInfo.setPosition(RectangleEdge.BOTTOM);
		this.latencyAnnotation = new XYTitleAnnotation(x, 0.58, latencyInfo, RectangleAnchor.TOP_LEFT);
		this.latencyAnnotation.setMaxWidth(0.9);
        this.subPlot.addAnnotation(this.latencyAnnotation, false);
        
        TextTitle utilizationInfo  = new TextTitle("Uti.:");
        utilizationInfo.setFont(new Font("SansSerif", Font.PLAIN, 9));
		utilizationInfo.setPaint(Color.GREEN);
		utilizationInfo.setPosition(RectangleEdge.BOTTOM);
		this.utilizationAnnotation = new XYTitleAnnotation(x, 0.48, utilizationInfo, RectangleAnchor.TOP_LEFT);
		this.utilizationAnnotation.setMaxWidth(0.9);
        this.subPlot.addAnnotation(this.utilizationAnnotation, false);
	}
	
	public void setSubInformation(double x, int index) {	
		TextTitle indexInfo        = new TextTitle("Index: " + index);
		indexInfo.setFont(new Font("SansSerif", Font.PLAIN, 9));
		indexInfo.setPaint(Color.GREEN);
		indexInfo.setPosition(RectangleEdge.BOTTOM);
		this.indexAnnotation = new XYTitleAnnotation(x, 0.98, indexInfo, RectangleAnchor.TOP_LEFT);
		this.indexAnnotation.setMaxWidth(0.9);
        this.subPlot.addAnnotation(this.indexAnnotation, false);
        
		TextTitle timeInfo         = new TextTitle("Time: " + this.transferByteSeries.getDataItem(index));
		timeInfo.setFont(new Font("SansSerif", Font.PLAIN, 9));
		timeInfo.setPaint(Color.GREEN);
		timeInfo.setPosition(RectangleEdge.BOTTOM);
		this.transferByteAnnotation = new XYTitleAnnotation(x, 0.88, timeInfo, RectangleAnchor.TOP_LEFT);
        this.transferByteAnnotation.setMaxWidth(0.9);
        this.subPlot.addAnnotation(this.transferByteAnnotation, false);
        
        TextTitle transferByteInfo = new TextTitle("Tran.: " + this.transferByteSeries.getValue(index));
		transferByteInfo.setFont(new Font("SansSerif", Font.PLAIN, 9));
		transferByteInfo.setPaint(Color.GREEN);
		transferByteInfo.setPosition(RectangleEdge.BOTTOM);
		this.activityAnnotation = new XYTitleAnnotation(x, 0.78, transferByteInfo, RectangleAnchor.TOP_LEFT);
		this.activityAnnotation.setMaxWidth(0.9);
        this.subPlot.addAnnotation(this.activityAnnotation, false);
        
        TextTitle activityInfo     = new TextTitle("Act.: " + this.activitySeries.getValue(index));
		activityInfo.setFont(new Font("SansSerif", Font.PLAIN, 9));
		activityInfo.setPaint(Color.GREEN);
		activityInfo.setPosition(RectangleEdge.BOTTOM);
		this.activityAnnotation = new XYTitleAnnotation(x, 0.68, activityInfo, RectangleAnchor.TOP_LEFT);
		this.activityAnnotation.setMaxWidth(0.9);
        this.subPlot.addAnnotation(this.activityAnnotation, false);
        
        TextTitle latencyInfo      = new TextTitle("Lat.: " + this.latencySeries.getValue(index));
		latencyInfo.setFont(new Font("SansSerif", Font.PLAIN, 9));
		latencyInfo.setPaint(Color.GREEN);
		latencyInfo.setPosition(RectangleEdge.BOTTOM);
		this.latencyAnnotation = new XYTitleAnnotation(x, 0.58, latencyInfo, RectangleAnchor.TOP_LEFT);
		this.latencyAnnotation.setMaxWidth(0.9);
        this.subPlot.addAnnotation(this.latencyAnnotation, false);
        
        TextTitle utilizationInfo  = new TextTitle("Uti.: " + this.utilizationSeries.getValue(index));
        utilizationInfo.setFont(new Font("SansSerif", Font.PLAIN, 9));
		utilizationInfo.setPaint(Color.GREEN);
		utilizationInfo.setPosition(RectangleEdge.BOTTOM);
		this.utilizationAnnotation = new XYTitleAnnotation(x, 0.48, utilizationInfo, RectangleAnchor.TOP_LEFT);
		this.utilizationAnnotation.setMaxWidth(0.9);
        this.subPlot.addAnnotation(this.utilizationAnnotation, false);
	}

	public XYPlot getSubPlot() {
		return this.subPlot;
	}
	
	public TimeSeries getTransferByteSeries() {
		return this.transferByteSeries;
	}
	
	public TimeSeries getLatencySeries() {
		return this.latencySeries;
	}
	
	public TimeSeries getUtilizationSeries() {
		return this.utilizationSeries;
	}
	
	public TimeSeries getActivitySeries() {
		return this.activitySeries;
	}
	
	public void fireSeriesChanged() {
		transferByteSeries.fireSeriesChanged();
	}
	
	public void setsubPlotRender(int index, XYItemRenderer renderer) {
		this.subPlot.setRenderer(index, renderer);
		
		/* Renderer */
		AbstractRenderer r1 = (AbstractRenderer) subPlot.getRenderer(0);
		AbstractRenderer r2 = (AbstractRenderer) subPlot.getRenderer(1);
		AbstractRenderer r3 = (AbstractRenderer) subPlot.getRenderer(2);
		
		r1.setSeriesPaint(0, Color.red, false);
		((DefaultXYItemRenderer)r1).setSeriesShapesVisible(0, false);
		
		r2.setSeriesPaint(0, Color.magenta, false);
		((DefaultXYItemRenderer)r2).setSeriesShapesVisible(0, false);
		
		r3.setSeriesPaint(0, Color.yellow, false);
		r3.setSeriesPaint(1, Color.blue, false);
	}
	
	public void UpdateMaximumItemAge(int maximumItemAge_ms) {
		this.maximumItemAge_ms = maximumItemAge_ms;
		this.transferByteSeries.setMaximumItemAge(this.maximumItemAge_ms);
		this.activitySeries.setMaximumItemAge(this.maximumItemAge_ms);
		this.latencySeries.setMaximumItemAge(this.maximumItemAge_ms);
		this.utilizationSeries.setMaximumItemAge(this.maximumItemAge_ms);
	}
	
	public void addBusTraffic(final long currentTime_ms, final double transferByte, final double activity, final double latency, final double utilization) {
		this.transferByteSeries.add(new FixedMillisecond(currentTime_ms), transferByte, false);
		this.activitySeries.add(new FixedMillisecond(currentTime_ms), activity, false);
		this.latencySeries.add(new FixedMillisecond(currentTime_ms), latency, false);
		this.utilizationSeries.add(new FixedMillisecond(currentTime_ms), latency + utilization, false);	
	}
	
	public void addBusTraffic(final long currentTime_ms, final double transferByte, final double activity, final double latency, final double utilization, boolean isSynchronized) {
		this.transferByteSeries.add(new FixedMillisecond(currentTime_ms), transferByte, isSynchronized);
		this.activitySeries.add(new FixedMillisecond(currentTime_ms), activity, isSynchronized);
		this.latencySeries.add(new FixedMillisecond(currentTime_ms), latency, isSynchronized);
		this.utilizationSeries.add(new FixedMillisecond(currentTime_ms), latency + utilization, isSynchronized);	
	}
	
	public void UpdateUpperValueOfRangeAxis(double upperRangeAxisLeftside, double upperRangeAxisRightside, double upperRangeAxisRightside2) {
		if (this.upperRangeAxisLeftside < upperRangeAxisLeftside) {
			this.upperRangeAxisLeftside  = upperRangeAxisLeftside;
			double maxRangeAxisLeftside  = this.upperRangeAxisLeftside;
			this.rangeAxisLeftside.setRange(0, maxRangeAxisLeftside);
		}
		if (this.upperRangeAxisRightside < upperRangeAxisRightside) {
			this.upperRangeAxisRightside = upperRangeAxisRightside;	
			double maxRangeAxisRightside = this.upperRangeAxisRightside;
			this.rangeAxisRightside.setRange(0, maxRangeAxisRightside);
		}
		if (this.upperRangeAxisRightside2 < upperRangeAxisRightside2) {
			this.upperRangeAxisRightside2 = upperRangeAxisRightside2;
			double maxRangeAxisRightside2 = this.upperRangeAxisRightside2;
			this.rangeAxisRightside2.setRange(0, maxRangeAxisRightside2);
		}
	}
	
	public void setAutoRange(boolean isAutoRange) {
		this.isAutoRange  = isAutoRange;
		if(this.isAutoRange) {
			this.rangeAxisLeftside.setAutoRange(true);
			
			this.rangeAxisRightside.setAutoRange(true);
			
			this.rangeAxisRightside2.setAutoRange(true);
		} else {
			this.rangeAxisLeftside.setAutoRange(false);
			this.rangeAxisLeftside.setRange(0, this.upperRangeAxisLeftside);
			
			this.rangeAxisRightside.setAutoRange(false);
			this.rangeAxisRightside.setRange(0, this.upperRangeAxisRightside);
			
			this.rangeAxisRightside2.setAutoRange(false);
			this.rangeAxisRightside2.setRange(0, this.upperRangeAxisRightside2);
		}
	}
  
}
